/*************************************************************************
|                                                                        |
|   INPUT.C                                                     27.06.94 |
|   ADMAKE Utility:  parse a makefile                                    |
|                                                                        |
*************************************************************************/

#ifdef VMS
#include STDIO
#include STRING
#include STDLIB
#include "h.h"
#else
#include <stdio.h>
#include <string.h>
#ifdef HEAP
#include <heap.h>
#endif
#include <stdlib.h>
#include "h.h"
#endif

struct name     namehead;
struct name *   firstname;

char            str1[LZ];               /* general store */
char            str2[LZ];

/*************************************************************************
|                                                                        |
|   >>>>  newname                                                        |
|                                                                        |
|   intern a name - return a pointer to the name struct                  |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
struct name * newname(char * name)
#else
struct name * newname(name)
char * name;
#endif

{
    struct name *           rp;
    struct name *           rrp;
    char *                  cp;
    register int            i;
    register char *         q;

    for
        (
        rp = namehead.n_next, rrp = &namehead;
        rp;
        rp = rp->n_next, rrp = rrp->n_next
        )
    {
        i = 0;
        q = rp->n_orig;
        while (name[i] == *(q+i))
            if (name[i++] == '\0') return rp;
    }

    if ((rp = (struct name *)malloc(sizeof(struct name))) == (struct name *)0)
        fatal(MSG[28]);
    rrp->n_next = rp;
    rp->n_next = (struct name *)0;
    if ((cp = malloc(strlen(name)+1)) == (char *)0) fatal(MSG[28]);
    strcpy(cp, name);
    rp->n_name = rp->n_orig = cp;
    rp->n_line = (struct line *)0;
    rp->n_time = (unsigned long)0;
    rp->n_flag = 0;

    return rp;
}

/*************************************************************************
|                                                                        |
|   >>>>  newdep                                                         |
|                                                                        |
|   add a dependant to the end of the supplied list of dependants,       |
|   return the new head pointer for that list                            |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
struct depend * newdep(struct name * np, struct depend * dp)
#else
struct depend * newdep(np, dp)
struct name * np;
struct depend * dp;
#endif

{
    register struct depend *rp;
    register struct depend *rrp;

    if ((rp = (struct depend *)malloc(sizeof(struct depend)))
        == (struct depend *)0)
        fatal(MSG[29]);
    rp->d_next = (struct depend *)0;
    rp->d_name = np;

    if (dp == (struct depend *)0)
        return rp;

    for (rrp = dp; rrp->d_next; rrp = rrp->d_next)
        ;

    rrp->d_next = rp;
    return dp;
}

/*************************************************************************
|                                                                        |
|   >>>>  newcmd                                                         |
|                                                                        |
|   add a command to the end of the supplied list of commands, return    |
|   the new head pointer for that list, and save the actual command      |
|   structure created in case lif lines are found                        |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
struct cmd * newcmd(char * str, struct cmd * cp, struct cmd * * crp)
#else
struct cmd * newcmd(str, cp, crp)
char * str;
struct cmd * cp;
struct cmd * * crp;
#endif

{
    register struct cmd *   rp;
    register struct cmd *   rrp;
    register char *         rcp;

    if ((rcp = strrchr(str, '\n')) != (char *)0) *rcp = '\0';   /* lose nl */
    while (pspace(*str)) str++;

    if (*str == '\0')               /* if nothing left, then exit */
    {
        *crp = (struct cmd *)0;
        return cp;
    }

    if ((rp = (struct cmd *)malloc(sizeof(struct cmd))) == (struct cmd *)0)
        fatal(MSG[30]);
    rp->c_next = (struct cmd *)0;
    if ((rcp = malloc(strlen(str)+1)) == (char *)0) fatal(MSG[30]);
    strcpy(rcp, str);
    rp->c_cmd = rcp;
    rp->c_lif = (struct lif *)0;
    *crp = rp;

    if (cp == (struct cmd *)0) return rp;

    for (rrp = cp; rrp->c_next; rrp = rrp->c_next)
        ;

    rrp->c_next = rp;
    return cp;
}

/*************************************************************************
|                                                                        |
|   >>>>  newlif                                                         |
|                                                                        |
|   add a lif line to the end of the supplied list of lif lines, return  |
|   the new head pointer for that list                                   |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
static struct lif * newlif(char * str, struct lif * lp)
#else
static struct lif * newlif(str, lp)
char * str;
struct lif * lp;
#endif

{
    register struct lif *   rp;
    register struct lif *   rrp;
    register char *         rlp;

    if ((rlp = strrchr(str, '\n')) != (char *)0) *rlp = '\0';   /* lose nl */

    if ((rp = (struct lif *)malloc(sizeof(struct lif))) == (struct lif *)0)
        fatal(MSG[31]);
    rp->f_next = (struct lif *)0;
    if ((rlp = malloc(strlen(str)+1)) == (char *)0) fatal(MSG[31]);
    strcpy(rlp, str);
    rp->f_lif = rlp;

    if (lp == (struct lif *)0) return rp;
    for (rrp = lp; rrp->f_next; rrp = rrp->f_next)
        ;

    rrp->f_next = rp;
    return lp;
}

/*************************************************************************
|                                                                        |
|   >>>>  newline                                                        |
|                                                                        |
|   add a new 'line' of stuff to a target, check to see if commands      |
|   already exist for the target                                         |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
VOID newline(struct name * np, struct depend * dp, struct cmd * cp, int flag)
#else 
VOID newline(np, dp, cp, flag)
struct name * np;
struct depend * dp;
struct cmd * cp;
int flag;
#endif

{
    bool                    hascmds = FALSE;  /*  Target has commands  */
    register struct line *  rp;
    register struct line *  rrp;

    if ( (!dp && !cp && !strcmp(np->n_name,".SUFFIXES")))
    {
        for (rp = np->n_line; rp; rp = rrp)
        {
            rrp = rp->l_next;
            free(rp);
        }
        np->n_line = (struct line *)0;
        np->n_flag &= ~N_TARG;
        return;
    }

    for
        (
        rp = np->n_line, rrp = (struct line *)0;
        rp;
        rrp = rp, rp = rp->l_next
        )
        if (rp->l_cmd)
            hascmds = TRUE;

/*****  handle the implicit rules redefinition case */

    if (hascmds && cp && !(np->n_flag & N_DOUBLE))
        if (np->n_name[0] == '.' && dp == (struct depend *)0)
        {
            np->n_line->l_cmd = cp;
            return;
        }
        else
            error(MSG[33], np->n_name); /* Multiple command sets */

    if (np->n_flag & N_TARG)
        if (!(np->n_flag & N_DOUBLE) != !flag)          /* like xor */
            error(MSG[34], np->n_name); /* Inconsistent rules for target */

    if ((rp = (struct line *)malloc(sizeof (struct line)))
        == (struct line *)0)
        fatal(MSG[32]);
    rp->l_next = (struct line *)0;
    rp->l_dep = dp;
    rp->l_cmd = cp;

    if (rrp)
        rrp->l_next = rp;
    else
        np->n_line = rp;

    np->n_flag |= N_TARG;

    if (flag)
        np->n_flag |= N_DOUBLE;
}

/*************************************************************************
|                                                                        |
|   >>>>  checklif                                                       |
|                                                                        |
|   check for the presence of a local input file                         |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
static VOID checklif(struct cmd * cp)
#else
static VOID checklif(cp)
struct cmd * cp;
#endif

{
    register struct lif *   lp;

    lp = (struct lif *)0;
    if (pstrstr(str2,"$-"))    /* lif follows */
    {
        for (;;)
        {
            if (getline(str2))
                fatal(MSG[35]);         /* Unterminated local input file */
            if (str2[0] == '<') break;
            lp = newlif(&str2[0],lp);
        }
    }
    if (cp) cp->c_lif = lp;
}

/*************************************************************************
|                                                                        |
|   >>>>  expast                                                         |
|                                                                        |
|   expand $* in dependencies, by creating a new dep for each            |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
static VOID expast(struct name * np)
#else
static VOID expast(np)
struct name * np;
#endif

{
    register struct depend *dp;
    register struct line *  lp;
    struct name *           nnp;
    struct depend * *       dpp;
    char *                  p;
    char *                  q;
    char *                  r;
    char *                  rr;
    char *                  suff;
    char                    expb[64];

    for (lp = np->n_line; lp; lp = lp->l_next)
    {

        for (dp = lp->l_dep; dp; dp = dp->d_next)
        {
            rr = (char *)0;
            p = r = dp->d_name->n_name;
            while (*p)
            {
                if ( ((*p & 0xff) == 0244) && ((*(p + 1) & 0xff) == 0255) )
                {
                    rr = p;
                    break;
                }
                p++;
            }
            if (rr != (char *)0)
            {
                q = np->n_name;                 /* get target name */
                if ((strlen(q) + strlen(r)) > 60) fatal(MSG[36]);
                p = expb;
                while (r < rr) *p++ = *r++;
                suff = suffix(q);
                while (q < suff) *p++ = *q++;
                r += 2;                         /* skip special marker */
                while (*r) *p++ = *r++;
                *p = '\0';
                nnp = newname(expb);            /* make a new dependant */
                for (dpp = &(lp->l_dep); *dpp; dpp = &((*dpp)->d_next))
                {
                    if (*dpp == dp)
                    {
                        *dpp = (struct depend *)malloc(sizeof(struct depend));
                        if (*dpp == (struct depend *)0) fatal(MSG[36]);
                        (*dpp)->d_next = dp->d_next;
                        (*dpp)->d_name = nnp;
                        break;
                    }
                }
            }
        }
    }
}

/*************************************************************************
|                                                                        |
|   >>>>  input                                                          |
|                                                                        |
|   parse input from the makefile, and construct a tree structure of it  |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
VOID input(void)
#else
VOID input()
#endif

{
    char *                  p;              /*  General  */
    char *                  q;
    struct name *           np;
    struct depend *         dp;
    struct cmd *            cp;
    struct cmd *            crp;
    bool                    dbl;

    if (getline(str1)) return;      /* read the first line */
    setmacro("*","\244\255");       /* special marker for $* */

    for(;;)
    {
        if ((tabreqd) ? (*str1 == '\t') : pspace(*str1))
            error(MSG[37]);     /* command with no target/dependency line */

        p = str1;
        while (pspace(*p)) p++;    /* find first target */

        while (((q = strchr(p, '=')) != (char *)0) &&
            (p != q) && (q[-1] == '\\'))        /*  Find value */
        {
            register char * a;

            a = q - 1;              /* del \ chr; move rest back */
            p = q;
            while ((*a++ = *q++) != '\0')
                ;
        }

        if (q != (char *)0)
        {
            register char * a;
            int      add_to;

            *q++ = '\0';            /* separate name and val */

            add_to = 0;
            if (q[-2] == '+')       /* add to macro */
            {
                add_to = 1;
                q[-2] = '\0';
            }
            else if (q[-2] == '-')  /* take from macro */
            {
                add_to = 2;
                q[-2] = '\0';
            }
            else if (q[-2] == ':')  /* immediate expand macro */
            {
                add_to = 3;
                q[-2] = '\0';
            }

            while (pspace(*q)) q++;
            if ((p = strrchr(q, '\n')) != (char *)0) *p = '\0';
            p = str1;
            if ((a = gettok(&p)) == (char *)0)
                error(MSG[38]);         /* Macro def. without macro name */
            if (add_to == 1) addtomacro(a, q);
            else if (add_to == 2) extmacro(a, q);
            else 
            {
                if (add_to == 3) expand(q);
                setmacro(a, q);
            }
            if (getline(str1)) return;
            continue;
        }

        expand(str1);
        p = str1;

#ifndef NIX
#ifndef VMS
        if ((str1[0]) && (!pspace(str1[0])) && 
            (str1[1] == ':') && 
            (str1[2]) && (!pspace(str1[2]))) p += 2;
#endif
#endif

        while (((q = strchr(p, ':')) != (char *)0) &&
               (p != q) && (q[-1] == '\\'))        /*  Find dependants  */
        {
            register char * a;

            a = q - 1;      /* del \ chr; move rest back */
            p = q;
            while ((*a++ = *q++) != '\0')
                ;
        }

        if (q == (char *)0) error(MSG[39]); /* Cannot find any targets */

        *q++ = '\0';                /* separate targets and dependants */

        if (*q == ':')              /* double colon */
        {
            dbl = 1;
            q++;
        }
        else
            dbl = 0;

        for (dp = (struct depend *)0; ((p = gettok(&q)) != (char *)0);)
         
        {                           /* get list of dep's */
            np = newname(p);        /* intern name */
            dp = newdep(np, dp);    /* add to dep list */
        }

        *((q = str1) + strlen(str1) + 1) = '\0';    /* two nulls for gettok */
        cp = (struct cmd *)0;
        if (getline(str2) == FALSE) /* get commands */
        {
            while ((tabreqd) ? (*str2 == '\t') : pspace(*str2))
            {
                cp = newcmd(&str2[0], cp, &crp);
                checklif(crp); 
                if (getline(str2)) break;
            }
        }

        while ((p = gettok(&q)) != (char *)0)   /* get list of targ's */
        {
            np = newname(p);                    /* intern name */
            newline(np, dp, cp, dbl);
            expast(np);                         /* expand $* in dep */
            if (!firstname && p[0] != '.') firstname = np;
        }

        if (nestlvl < 0) return;                /* eof? */
        strcpy(str1, str2);
    }
}
